#ifndef DW_GRAPHICS_VERTEX_FORMAT_H
#	define DW_GRAPHICS_VERTEX_FORMAT_H

namespace dw {
	namespace graphics {

struct VertexElement
{
	static const dw::uint32 SemanticMaxIndex = 3;

	enum types
	{
		// scalar
		int8 = 0, uint8, int16, uint16, int32, uint32, float32, float64,
		// vector
		ivec2, uvec2, vec2, dvec2,
		ivec3, uvec3, vec3, dvec3,
		ivec4, uvec4, vec4, dvec4,
		// matrix
		mat2, dmat2, mat2x3, dmat2x3, mat2x4, dmat2x4,
		mat3, dmat3, mat3x2, dmat3x2, mat3x4, dmat3x4,
		mat4, dmat4, mat4x2, dmat4x2, mat4x3, dmat4x3,

		types_count
	};

	enum semantics
	{
		position = 0,
		normal,
		texcoord,
		color,
		user,

		semantics_count
	};

	static dw::uint32 getHashcode(semantics semantic, dw::uint index);
	static types itemsType(types t);
	static dw::uint32 itemsCount(types t);
	static dw::uint32 itemsSize(types t);
	static dw::uint32 size(types t);

	static GLenum glType(types t);
	static GLenum glItemsType(types t);

	types type;
	semantics semantic;
	dw::uint32 semanticIndex;
};

class VertexFormat
{
public :
	struct Component
	{
		VertexElement decl;
		dw::uint32 offset;
	};

public :
	VertexFormat(const VertexElement* elements, dw::uint elementsCount)
	:	m_components(elementsCount)
	,	m_hashcode(0)
	,	m_vertexSize(0)
	{
		dwAssert(elements != NULL, "Invalid parameter: elements is null");

		for (dw::uint32 elementIt = 0; elementIt < elementsCount; ++elementIt)
		{
			Component& component = m_components[elementIt];
			const VertexElement& ref = elements[elementIt];

			dwAssert(!hasComponent(ref.semantic, ref.semanticIndex), "Invalid parameter: multiple elements share the same semantic and semanticIndex");
			dwAssert(ref.semanticIndex < VertexElement::SemanticMaxIndex, "Invalid declaration, semanticIndex is out of bounds");

			component.decl = ref;
			component.offset = m_vertexSize;

			m_hashcode |= VertexElement::getHashcode(ref.semantic, ref.semanticIndex);
			m_vertexSize += VertexElement::size(ref.type);
		}
	}

public :
	const dw::uint32 getVertexSize() const
	{
		return m_vertexSize;
	}

	const dw::uint32 getComponentsCount() const
	{
		return m_components.size();
	}

	const Component& getComponent(dw::uint32 index) const
	{
		dwAssert(index < m_components.size(), "Invalid parameter: index is out of bounds");
		
		return m_components[index];
	}
	
	bool hasComponent(const VertexElement::semantics semantic, dw::uint32 semanticIndex) const
	{
		dw::uint32 hashcode = VertexElement::getHashcode(semantic, semanticIndex);

		return (m_hashcode & hashcode) == hashcode;
	}

private :
	typedef std::vector< Component > ComponentsCollection;

private :
	ComponentsCollection m_components;
	dw::uint32 m_hashcode;
	dw::uint32 m_vertexSize;
};

	} // namespace graphics
} // namespace dw

#include "graphics/vertexformat.inl"

#endif // !DW_GRAPHICS_VERTEX_FORMAT_H